package netvethpairs

import (
	"log"
	"os"
	"os/exec"
	"runtime"

	"github.com/vishvananda/netlink"
	"github.com/vishvananda/netns"
)

const VETH_LINK_A_0 = "veth100"
const VETH_LINK_A_1 = "veth101"
const VETH_LINK_B_0 = "veth200"
const VETH_LINK_B_1 = "veth201"

func init() {
	log.SetPrefix("[veth-pairs]")
}

func Run() {
	runtime.LockOSThread()
	log.Printf("[在宿主机中]先保留初始状态的netns,待会NewNamed之后切回这个初始netns...")
	oldNetNs, err := netns.Get()
	if err != nil {
		log.Fatalf("[在宿主机]获取原始netns失败: %v\n", err)
	}
	defer oldNetNs.Close()
	tmpCmd2 := exec.Command("ip", "link")
	tmpCmd2.Stdout = os.Stdout
	tmpCmd2.Stderr = os.Stderr
	tmpCmd2.Run()

	// 创建一个新的匿名的netns; 注意New操作会切换当前进程的网络命名空间
	// 官方注释：NewNamed creates a new named network namespace, sets it as current, and returns a handle to it
	newNetNs, err := netns.NewNamed("ns-test")
	if err != nil {
		log.Fatalf("[在宿主机中]新建匿名的网络命名空间失败: %v\n", err)
	}
	defer newNetNs.Close()
	log.Println("[在宿主机中]创建新的匿名网络命名空间，!!!!!并切换到这个新的netns")

	tmpCmd := exec.Command("ip", "link")
	tmpCmd.Stdout = os.Stdout
	tmpCmd.Stderr = os.Stderr
	tmpCmd.Run()

	log.Println("[在匿名netns中]切换回宿主机初始netns")
	if err := netns.Set(oldNetNs); err != nil {
		log.Fatalln("[在匿名netns中]切换回宿主机初始netns失败!!!")
	}

	// 创建一个新的 veth pairs
	veth100 := &netlink.Veth{
		// 偶数端除了MTU其余都默认属性
		LinkAttrs: netlink.LinkAttrs{
			Name: VETH_LINK_A_0,
			MTU:  1500, // MTU（Maximum Transmission Unit），即最大传输单元。在早期的以太网规范（IEEE 802.3）里，就把 MTU 设定在了 1500 字节 。
		},
		// 奇数端
		PeerName: VETH_LINK_A_1,
	}
	if err := netlink.LinkAdd(veth100); err != nil {
		log.Fatalf("[在宿主机]无法创建veth-pair:%v\n", err)
	}
	log.Printf("[在宿主机]新建veth-pair...%s~%s\n", VETH_LINK_A_0, VETH_LINK_A_1)

	// 获取刚创建的两个虚拟网络接口
	// 虚拟网络接口也是 net link(网络链路)的一种，此外还有物理网卡、网桥等
	linkA0, err := netlink.LinkByName(VETH_LINK_A_0)
	if err != nil {
		log.Fatalf("[在宿主机]netlink.LinkByName 获取A_0失败了: %v\n", err)
	}
	linkA1, err := netlink.LinkByName(VETH_LINK_A_1)
	if err != nil {
		log.Fatalf("[在宿主机]netlink.LinkByName 获取A_1失败了: %v\n", err)
	}

	// 在宿主机中把偶数端移动到新的netns中
	log.Printf("%#v\n", linkA0)
	if err := netlink.LinkSetNsFd(linkA0, int(newNetNs)); err != nil {
		log.Fatalf("[在宿主机中]把偶数端veth-pair失败移动到的新的匿名空间中:%v\n", err)
	}

	log.Println("[在宿主机中]给新的netns设置偶数端veth-pair")
	// 在宿主机中激活veth-pair的奇数端
	if err := netlink.LinkSetUp(linkA1); err != nil {
		log.Fatalf("[在宿主机中]激活veth-pairs的奇数端:%v\n", err)
	}
	log.Println("[在宿主机中]激活veth-pairs的奇数端")

	// 切换进入匿名的 netns 中
	err = netns.Set(newNetNs)
	if err != nil {
		log.Fatalf("Failed to switch to new namespace: %v\n", err)
	}
	log.Println("进入匿名空间......")
	// 在匿名空间中，重新获取网络接口的文件描述符，因为可能填进来之后发生了变化
	linkA0InNs, err := netlink.LinkByName(VETH_LINK_A_0)
	if err != nil {
		log.Fatalf("[在匿名空间]获取接口偶数端的文件描述符失败: %v", err)
	}
	if err := netlink.LinkSetUp(linkA0InNs); err != nil {
		log.Fatalf("[在匿名空间]激活偶数端失败: %v", err)
	}
	log.Println("[在匿名空间]激活偶数端")

	// 返回原始空间，查看虚拟网络接口，应该能看到存在 veth1,且状态是激活状态
	netns.Set(oldNetNs)
	tmpCmd3 := exec.Command("ip", "link")
	tmpCmd3.Stdout = os.Stdout
	tmpCmd3.Stderr = os.Stderr
	tmpCmd3.Run()
}
